package com.cougil.uoc.seghismed.cipher;

import iaik.asn1.ASN1Object;
import iaik.asn1.CodingException;
import iaik.asn1.DerCoder;
import iaik.asn1.structures.AlgorithmID;
import iaik.pkcs.PKCSException;
import iaik.pkcs.pkcs7.IssuerAndSerialNumber;
import iaik.pkcs.pkcs7.SignedData;
import iaik.pkcs.pkcs7.SignerInfo;
import iaik.x509.X509Certificate;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SignatureException;

import org.apache.log4j.Logger;

/**
* Classe que permet encapsular la lògica relacionada amb xifrar, desxifrar, signar i verificar la 
* informació signada, emprant la clau privada i el conjunt de certificats per als destinataris associats
* @author Ignacio Cougil Jares
* @author <a href="mailto:jcastellar@uoc.edu">Jordi Castella-Roca</a>
*/
public class SignerManager {
	
	private static final Logger logger = Logger.getLogger(SignerManager.class);

	private PrivateKey privateKey;
	private X509Certificate[] chain;

	private SignerManager() {
		super();
	}
	
	/**
	 * Crea una nova instància emprada per xifrar i desxifrar informació emprant la clau privada,
	 * el conjunt de certificats per signar i els certificats dels destinataris trobats dintre del 
	 * contenidor de tipus PKCS#12 que es carregarà amb el nom i contrasenya informats
	 * @param fileName Nom del contenidor PKCS#12 que es preten carregar 
	 * @param password Paraula clau per al contenidor PKCS#12 que es preten carregar
	 * @throws PKCSException 
	 * @throws IOException 
	 */
	public SignerManager(String fileName, String password) throws IOException, PKCSException {
		P12 p12 = new P12(fileName, password);
		privateKey = p12.getPrivateKey();
		chain = p12.getCertificates();
	}

	/**
	 * Describe <code>signData</code> method here.
	 * 
	 * @param dataIn
	 *            a <code>String</code> value
	 * @return a <code>byte[]</code> value
	 * @throws NoSuchAlgorithmException 
	 * @throws PKCSException 
	 */
	public byte[] signData(String dataIn) throws NoSuchAlgorithmException, PKCSException {

		byte[] p7Enc = null;
		SignedData p7 = new SignedData(dataIn.getBytes(), SignedData.EXPLICIT);
		p7.setCertificates(chain);
		SignerInfo signer = new SignerInfo(
				new IssuerAndSerialNumber(chain[0]), AlgorithmID.sha1,
				privateKey);

		p7.addSignerInfo(signer);

		p7Enc = p7.getEncoded();

		return p7Enc;
	}

	/**
	 * Describe <code>verifySignature</code> method here.
	 * 
	 * @param p7
	 *            a <code>byte[]</code> value
	 * @param data
	 *            a <code>byte[]</code> value
	 * @return Retorna la llista de certificats dels signants
	 * @throws CodingException 
	 * @throws NoSuchAlgorithmException 
	 * @throws PKCSException 
	 * @throws SignatureException 
	 */
	public X509Certificate[] verifySignature(byte[] p7, byte[] data) throws CodingException, NoSuchAlgorithmException, PKCSException, SignatureException {

		AlgorithmID[] algIDs = { AlgorithmID.sha1, AlgorithmID.md5 };

		SignedData signature =  new SignedData(data, algIDs);

		ASN1Object objExpP7 = DerCoder.decode(p7);
		signature.decode(objExpP7);

		SignerInfo[] signerInfos = signature.getSignerInfos();
		X509Certificate certifs[] = new X509Certificate[signerInfos.length];  
		for (int i = 0; i < signerInfos.length; i++) {
			certifs[i] = signature.verify(i);
			logger.info("Signatura OK del signant: "+certifs[i].getSubjectDN());
		}
		return certifs;
	}

}
